package os.kernelProcess.schedulers;

import java.util.Enumeration;
import java.util.Hashtable;

import os.MiniProcess;
import os.MiniThread;
import os.kernelProcess.SchedulerThread;
import os.kernelProcess.queues.FCFSQueue;

public class RoundRobinScheduler extends SchedulerThread
{

	public RoundRobinScheduler(MiniProcess process, String tid, int quantum)
	{
		super(process, tid, quantum);
		this.quantum = quantum;
		readyQueue = new FCFSQueue();
		blockedQueue = new Hashtable<String, MiniThread>();
		suspendedQueue = new Hashtable<String, MiniThread>();
	}

	/**
	 * Method that handles the scheduling of threads.
	 */
	// tested
	@Override
	public void schedule()
	{
		// XXX Done RoundRobinScheduler::schedule()
		while (true) {
			MiniThread head = null;
			try {
				while (readyQueue.getSize() == 0) {
//					 System.out.println(readyQueue.getSize());
				}
				head = readyQueue.getHead();
				runningThread = head;
//				System.err.println("Thread no "+head.getTid()+" is entering running state now");
				// view.displayMessage("Thread no "+head.getTid()+" is entering running state now");
				head.setThreadState(MiniThread.THREAD_RUNNING);
				readyQueue.removeThread(head.getTid());
				head.resumeExecution();
				sleep(quantum);
				if (head.getThreadState() == MiniThread.THREAD_RUNNING) {
					// System.out.println("Thread no "+head.getTid()+" is leaving the processor now");
					// view.displayMessage("Thread no "+head.getTid()+" is leaving the processor now");
					head.stopExecution();
					head.setThreadState(MiniThread.THREAD_READY);
					// System.out.println("Thread no "+head.getTid()+" is being appended to ready queue");
					// view.displayMessage("Thread no "+head.getTid()+" is being appended to ready queue");
					readyQueue.append(head);
				}
			} catch (InterruptedException e) {
				System.out.println("schedule interrupted before quantum");
			}
		}
	}

	@Override
	public void passThreadArguments(Object[] arguments) {
	}


	// XXX tested
	public void threadTerminated() {
		interrupt();
	}
	public void terminateProcess(MiniProcess process) {
		// XXX not tested SchedulerThread::terminateProcess
		Hashtable<String, MiniThread> threads = process.getThreads();
		Enumeration<MiniThread> e = threads.elements();
		while (e.hasMoreElements()) {
			terminateThread(e.nextElement());
		}
	}

	
	public void terminateThread(MiniThread thread) {
		// XXX: not tested MiniScheduler::terminateThread()
		thread.getProcess().threadTerminated(thread.getTid());
		int threadState = thread.getThreadState();
		switch (threadState) {
		case THREAD_SUSPENDED_BLOCKED:
			suspendedQueue.remove(thread.getTid());
			break;
		case THREAD_SUSPENDED_READY:
			suspendedQueue.remove(thread.getTid());
			break;
		case THREAD_BLOCKED:
			blockedQueue.remove(thread.getTid());
			break;
		default:
			readyQueue.removeThread(thread.getTid());
			break;
		}
	}
	// tested
	public void blockThread() {
		// XXX Done MiniScheduler::blockThread()
		runningThread.stopExecution();
		runningThread.setThreadState(THREAD_BLOCKED);
		readyQueue.removeThread(runningThread.getTid());
		blockedQueue.put(runningThread.getTid(), runningThread);
		interrupt();
	}

	
	// tested
	public void unblockThread(MiniThread thread)
	{
		// XXX: Done MiniScheduler::unblockThread()
		int state = thread.getThreadState();
		switch (state) {
		case THREAD_BLOCKED:
			blockedQueue.remove(thread.getTid());
			thread.setThreadState(THREAD_READY);
			readyQueue.append(thread);
			break;
		case THREAD_SUSPENDED_BLOCKED:
			thread.setThreadState(THREAD_SUSPENDED_READY);
			break;
		}
	}
	// tested
	public void suspendThread(MiniThread thread) {
		// XXX Done MiniScheduler::suspendThread()
		thread.stopExecution();
		int state = thread.getThreadState();
		switch (state) {
		case THREAD_BLOCKED:
			blockedQueue.remove(thread.getTid());
			thread.setThreadState(THREAD_SUSPENDED_BLOCKED);
			break;
		case THREAD_RUNNING:
			thread.setThreadState(THREAD_SUSPENDED_READY);
			interrupt();
			break;
		case THREAD_READY:
			readyQueue.removeThread(thread.getTid());
			thread.setThreadState(THREAD_SUSPENDED_READY);
			break;
		}
		suspendedQueue.put(thread.getTid(), thread);
	}
	// tested
	public void resumeThread(MiniThread thread) {
		// XXX Done MiniScheduler::resumeThread()
		int state = thread.getThreadState();
		suspendedQueue.remove(thread.getTid());
		if (state == THREAD_SUSPENDED_BLOCKED) {
			thread.setThreadState(THREAD_BLOCKED);
			blockedQueue.put(thread.getTid(), thread);
		} else {
			thread.setThreadState(THREAD_READY);
			readyQueue.append(thread);
		}
	}

	@Override
	public void threadStopped(MiniThread thread) {
		readyQueue.append(runningThread);
		runningThread.setThreadState(MiniThread.THREAD_READY);
		runningThread = null;
		interrupt();
		
	}

	public String getSchedulerName()
	{
		return "Round Robin";
	}

	@Override
	public int getQuantum() {
		return quantum;
	}
}
